#ifndef GUARD_BATTLE_AI_HELPER
#define GUARD_BATTLE_AI_HELPER


struct DamageCalc
{
	u8 bankAtk;
	u8 bankDef;
	struct Pokemon* monAtk;
	struct Pokemon* monDef;

	u16 atkSpecies;
	u16 defSpecies;

	u16 atkAbility;
	u16 defAbility;
	u16 atkPartnerAbility;
	u16 defPartnerAbility;

	u8 atkItemEffect;
	u8 atkItemQuality;
	u8 defItemEffect;
	u8 defItemQuality;

	u16 atkItem;

	u16 atkHP;
	u16 defHP;
	u16 atkMaxHP;
	u16 defMaxHP;
//	u32 attack;
//	u32 spAttack;
	u32 defense;
	u32 spDefense;
	u32 atkSpeed;
	u32 defSpeed;

	u8 atkBuff;
	u8 spAtkBuff;
	u8 defBuff;
	u8 spDefBuff;

	u32 atkStatus1;
	u32 defStatus1;
	u32 atkStatus3;
	u32 defStatus3;
	u16 defSideStatus;
	bool8 atkIsGrounded;
	bool8 defIsGrounded;

	u16 move;
	u8 moveType;
	u8 moveSplit;
	u8 resultFlags;
	u8 basePower;

	u8 specialFlags;
	bool8 attackerLoaded;
	bool8 defenderLoaded;
};
u16 GetBankItemEffect(u8 bank);
#define ABILITY_NONE 0
#define SPECIES_NONE 0
#define MOVE_NONE 0
#define ITEM_NONE 0

#define SIDE(bank) (gBattlerPositions[bank] & 1)
#define PARTNER(bank) (bank ^ BIT_FLANK)
#define FOE(bank) ((bank ^ BIT_SIDE) & BIT_SIDE)
#define ABILITY(bank) GetBattlerAbility(bank)
#define SPECIES(bank) gBattleMons[bank].species
#define ITEM(bank) gBattleMons[bank].item
#define ITEM_EFFECT(bank) GetBankItemEffect(bank)
extern u8 ItemId_GetHoldEffectParam(u16 item);
#define ITEM_QUALITY(bank) ItemId_GetHoldEffectParam(gBattleMons[bank].item)
#define ITEM_POCKET(bank) GetPocketByItemId(gBattleMons[bank].item)
#define SPLIT(move) gBattleMoves[move].split
#define CONSUMED_ITEMS(bank) gBattleStruct->usedHeldItems[bank]
#define SAVED_CONSUMED_ITEMS(bank) gBattleStruct->usedHeldItems[bank]
#define CHOICED_MOVE(bank) gBattleStruct->choicedMove[bank]
#define SECOND_OPPONENT (VarGet(VAR_SECOND_OPPONENT))
#define ABILITY_PRESENT(ability) AbilityBattleEffects(ABILITYEFFECT_CHECK_ON_FIELD, 0, ability, 0, 0)
#define NO_MOLD_BREAKERS(ability, move) (ability != ABILITY_MOLD_BREAKER && ability != ABILITY_TERAVOLT && ability != ABILITY_TURBOBLAZE && !CheckTableForMove(move, gMoldBreakerMoves))
#define IS_BLANK_TYPE(type) (type == TYPE_MYSTERY || type == TYPE_ROOSTLESS || type == TYPE_BLANK)
#define IS_TRANSFORMED(bank) (gBattleMons[bank].status2 & STATUS2_TRANSFORMED)
#define IS_BEHIND_SUBSTITUTE(bank) (gBattleMons[bank].status2 & STATUS2_SUBSTITUTE)
#define TOOK_DAMAGE(bank) (gSpecialStatuses[bank].moveturnLostHP_physical || gSpecialStatuses[bank].moveturnLostHP_special)
#define MOVE_HAD_EFFECT (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT))
#define PINCH_BERRY_CHECK(bank) (gBattleMons[bank].hp <= gBattleMons[bank].maxHP / 4 || (ABILITY(bank) == ABILITY_GLUTTONY && gBattleMons[bank].hp <= gBattleMons[bank].maxHP / 2))
#define STAT_CAN_RISE(bank, stat) ((gBattleMons[bank].statStages[stat-1] < 12 && ABILITY(bank) != ABILITY_CONTRARY) || (ABILITY(bank) == ABILITY_CONTRARY && gBattleMons[bank].statStages[stat-1] > 0))
#define STAT_CAN_FALL(bank, stat) ((gBattleMons[bank].statStages[stat-1] > 0 && ABILITY(bank) != ABILITY_CONTRARY) || (ABILITY(bank) == ABILITY_CONTRARY && gBattleMons[bank].statStages[stat-1] < 12))
#define BATTLER_ALIVE(bank) (gBattleMons[bank].hp > 0)
//#define BATTLER_MAX_HP(bank) (gBattleMons[bank].hp == gBattleMons[bank].maxHP)
#define BATTLER_SEMI_INVULNERABLE(bank) (gStatuses3[bank] & STATUS3_SEMI_INVULNERABLE)
#define IS_Z_MOVE(move) (move >= FIRST_Z_MOVE && move <= LAST_Z_MOVE)
#define IS_DOUBLE_BATTLE (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
#define IS_SINGLE_BATTLE !IS_DOUBLE_BATTLE
#define IS_GHOST_BATTLE ((gBattleTypeFlags & (BATTLE_TYPE_SCRIPTED_WILD_1 | BATTLE_TYPE_GHOST)) == BATTLE_TYPE_GHOST)

#define STAT_STAGE(bank, stat) (gBattleMons[bank].statStages[stat - 1])
#define MOVE_TARGET_ALL MOVE_TARGET_FOES_AND_ALLY
#define SpeedCalc GetBattlerTotalSpeedStat
#define CheckGrounding IsBattlerGrounded
#define ABILITY_ON_OPPOSING_FIELD IsAbilityOnOpposingSide
#define AI_THINKING_STRUCT ((struct AI_ThinkingStruct *)(gBattleResources->ai))
#define BATTLE_HISTORY ((struct BattleHistory *)(gBattleResources->battleHistory))
#define STATUS3_SKY_DROP_TARGET 0
#define MON_CAN_BATTLE(mon) (((GetMonData(mon, MON_DATA_SPECIES, NULL) != SPECIES_NONE && !GetMonData(mon, MON_DATA_IS_EGG, NULL) && GetMonData(mon, MON_DATA_HP, NULL) != 0)))

#define GetAbilityLocation(battler) (&gBattleMons[battler].ability)

#define APPLY_STAT_MOD(var, mon, stat, statIndex)                                   	\
{                                                                                   	\
    (var) = (stat) * (gStatStageRatios)[(mon)->statStages[(statIndex)-1]][0];         	\
    (var) = ((var) / (gStatStageRatios)[(mon)->statStages[(statIndex)-1]][1]);     \
}

#define APPLY_QUICK_STAT_MOD(var, buff)                 \
{                                                       \
    (var) = (var) * gStatStageRatios[buff][0];          \
    (var) = ((var) / gStatStageRatios[buff][1]);   \
}
enum
{
    STAT_STAGE_HP,       // 0
    STAT_STAGE_ATK,      // 1
    STAT_STAGE_DEF,      // 2
    STAT_STAGE_SPEED,    // 3
    STAT_STAGE_SPATK,    // 4
    STAT_STAGE_SPDEF,    // 5
    STAT_STAGE_ACC,      // 6
    STAT_STAGE_EVASION,  // 7
};
bool8 IsMagicRoomActive(void);
u16 GetMonItemEffect(struct Pokemon* mon);
bool8 IsPinchBerryItemEffect(u8 itemEffect);
bool8 IsGem(u16 item);
void PopulateDamageCalcStructWithBaseAttackerData(struct DamageCalc* data);
void PopulateDamageCalcStructWithBaseDefenderData(struct DamageCalc* data);
bool8 CheckContact(u16 move, u8 bank);
u32 AccuracyCalc(u16 move, u8 bankAtk, u8 bankDef);
u8 CountAliveMonsInBattle2(u8 caseId, u8 bankAtk, u8 bankDef);
bool8 IsAnyMaxMove(u16 move);
bool8 CheckTableForMove(u16 move,const u16 *table);
bool8 CanBeConfused(u8 bank, u8 checkSafeguard);
bool32 IsGravityPreventingMove(u32 move);
u8 CheckMoveLimitationsFromParty(struct Pokemon* mon, u8 unusableMoves, u8 check);
u8 AI_SpecialTypeCalc(u16 move, u8 bankAtk, u8 bankDef);
u8 AttacksThisTurn(u8 bank, u16 move) ;
u8 GetMoveTypeSpecial(u8 bankAtk, u16 move);
u16 GetAIAbility(u8 bankAtk, u8 bankDef, u16 move);
u16 IsValidMovePrediction(u8 bankAtk, u8 bankDef);
u16 GetBankMegaFormAbility(u8 bank);
bool8 IsAbilitySuppressed(u8 bank);
u8 TryRandomizeAbility(u8 ability, u16 species);
bool8 IsOfType(u32 battler, u8 type);
bool8 CheckSoundMove(u16 move);
bool8 MoveIgnoresSubstitutes(u16 move, u8 atkAbility);
bool8 MoveBlockedBySubstitute(u16 move, u8 bankAtk, u8 bankDef);
u8 FindMovePositionInMoveset(u16 move, u8 bank);
u8 GetDynamaxHPBoost(u8 battler);
bool8 MonMoveBlockedBySubstitute(u16 move, struct Pokemon* monAtk, u8 bankDef);
u32 AI_CalcDmg(const u8 bankAtk, const u8 bankDef, const u16 move, struct DamageCalc* damageData);
u32 AI_CalcPartyDmg(u8 bankAtk, u8 bankDef, u16 move, struct Pokemon* mon, struct DamageCalc* damageData);
u32 AI_CalcMonDefDmg(u8 bankAtk, u8 bankDef, u16 move, struct Pokemon* monDef, struct DamageCalc* damageData);
u8 CalcMoveSplit(u8 bank, u16 move);
s32 BracketCalc(u8 bank);
bool8 IsTrickRoomActive(void);
bool8 IsDynamaxed(u8 battler);
bool8 MoveInMoveset(u16 move, u8 bank);
u16 GetBaseMaxHP(u8 bank);
u16 GetBaseCurrentHP(u8 bank);
bool8 TakesDamageFromSandstorm(u8 bank);
bool8 TakesDamageFromHail(u8 bank);
u32 CalcStealthRockDamagePartyMon(struct Pokemon* mon);
u32 CalcSpikesDamagePartyMon(struct Pokemon* mon, u8 side);
u8 GetBattlerPosition(u8 battlerId);
struct Pokemon* LoadPartyRange(u8 bank, u8* firstMonId, u8* lastMonId);
u16 GetMaxMove(u32 battlerId, u8 index);
u16 CalcMaxMove(const struct Evolution *evolution, u16 move);
u16 GetMaxMoveByMove(u32 battlerId, u16 move);
bool8 ToUseZMove(u32 battlerId);
bool8 ToDyanamx(u32 battlerId);
bool8 UsedZMove(u32 battler);
bool8 IsBattlerDynamaxEnd(u32 battler);
u16 ReplaceWithZMoveRuntime(u8 bankAtk, u16 move);
u16 GetZMoveByMove(u32 bankAtk, u16 move);
bool8 IsFairyLockActive(void);
u8 ViableMonCountFromBank(u8 bank);
bool8 IsTagBattle(void);
u8 ViableMonCountFromBankLoadPartyRange(u8 bank);
bool8 IsTwoOpponentBattle(void);
bool8 BankSideHasSeaOfFire(u8 bank);
bool8 BankSideHasGMaxVineLash(u8 bank);
bool8 BankSideHasGMaxWildfire(u8 bank);
bool8 BankSideHasGMaxCannonade(u8 bank);
bool8 BankSideHasGMaxVolcalith(u8 bank);
u32 GetSandstormDamage(u8 bank);
u32 GetHailDamage(u8 bank);
u32 GetNightmareDamage(u8 bank);
u32 GetTrapDamage(u8 bank);
u32 GetLeechSeedDamage(u8 bank);
u32 GetPoisonDamage(u8 bank);
u32 GetBurnDamage(u8 bank);
u32 GetCurseDamage(u8 bank);
u32 GetSeaOfFireDamage(u8 bank);
u32 GetGMaxVineLashDamage(u8 bank);
u32 GetGMaxWildfireDamage(u8 bank);
u32 GetGMaxCannonadeDamage(u8 bank);
u32 GetGMaxVolcalithDamage(u8 bank);
bool8 BankSideHasRainbow(u8 bank);
bool8 CanBePoisoned(u8 bankDef, u8 bankAtk, bool8 checkFlowerVeil);
bool8 CanBeParalyzed(u8 bank, bool8 checkFlowerVeil);
bool8 CanBeBurned(u8 bank, bool8 checkFlowerVeil);
bool8 CanBeFrozen(u8 bank, bool8 checkFlowerVeil);
bool8 CanBePutToSleep(u8 bank, bool8 checkFlowerVeil);
bool8 DoesProtectionMoveBlockMove(u8 bankAtk, u8 bankDef, u16 atkMove, u16 protectMove);
u8 CalcMoveSplitFromParty(struct Pokemon* mon, u16 move);
bool8 CanDynamax(u8 bank);
bool8 CanKnockOffItem(u8 bank);
u16 TypeCalc(u16 move, u8 bankAtk, u8 bankDef, struct Pokemon* monAtk, bool8 CheckParty);
bool8 CanBeInfatuated(u8 bankDef, u8 bankAtk);
bool8 IsBerry(u16 item);
bool8 StatsMaxed(u8 bank);
bool8 MonCanUseMaxMoveWithEffect(struct Pokemon* mon, u8 maxEffect);
bool8 MoveInMonMovesetThatCanChangeByGigantamaxing(struct Pokemon* mon);
bool32 MonCanDynamax(struct Pokemon* mon);
bool8 IsMockBattle(void);
u16 SpeedCalcMon(u8 side, struct Pokemon* mon);
bool8 BankSideHasTwoTrainers(u8 bank);
bool8 AnyStatGreaterThan(u8 bank, u8 amount);
bool8 SandstormHurts(u8 bank);
bool8 HailHurts(u8 bank);
u16 AI_TypeCalc(u16 move, u8 bankAtk, struct Pokemon* monDef);
s8 PriorityCalcMon(struct Pokemon* mon, u16 move);
bool8 CheckGroundingFromPartyData(struct Pokemon* mon);
bool8 WillFaintFromEntryHazards(struct Pokemon* mon, u8 side);
bool8 IsGravityActive();
bool8 IsTypeZCrystal(u16 item, u8 moveType);
bool8 IsMegaZMoveBannedBattle(void);
u8 GetTerrainType();
bool8 IsConfused(u8 bank);
#define gTerrainType GetTerrainType()
bool8 CanPartyMonBePoisoned(struct Pokemon* mon);
bool8 IsAffectedByPowder(u8 bank);
u16 GetNaturePowerMove();
bool8 CheckTableForMoveEffect(u16 move, const u16 table[]);
bool8 IsMoveRedirectionPrevented(u16 move, u8 atkAbility);
bool8 CantUseSoundMoves(u8 bank);
bool8 IsHealBlocked(u8 bank);
bool32 IsHealBlockPreventingMove(u32 battler, u32 move);
bool8 IsMaxMoveWithStatusEffect(u16 move);
bool8 BankSideHasMist(u8 bank);
bool8 IsMaxMoveWithEffect(u16 move, u8 effect);
bool8 IsMaxMoveWithConfusionEffect(u16 move);
bool8 IsLaserFocused(u8 bank);
bool8 IsMaxMoveWithWeatherEffect(u16 move);
bool8 BankSideHasSafeguard(u8 bank);
bool8 IsWonderRoomActive();
bool8 IsIonDelugeActive(void);
bool8 CanBeTormented(u8 bank);
bool8 IsTaunted(u8 bank);
bool8 CheckTableForAbility(u16 ability, const u16 table[]);
bool8 CanFling(u16 item, u16 species, u8 ability, u8 bankOnSide, u8 embargoTimer);
bool8 IsMudSportActive(void);
bool32 AreStatsMaxed(u8 battlerId, u8 n);
bool8 MainStatsMaxed(u8 bank);
bool8 IsMaxMoveWithTerrainEffect(u16 move);
bool8 IsWaterSportActive(void);
u8 GetCamouflageType();
bool8 IsBankHoldingFocusSash(u8 bank);
s32 CalcStrengthSapHealAmount(u8 bankAtk, u8 bankDef);
#endif